#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <cstdio>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <cstring>
#include "InetAddress.h"
#include "Sock.h"
#include "Epoll.h"

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        printf("usage: ./tcpepoll  port\n");
        printf("example: ./tcpepoll 8080\n\n");
        return -1;
    }

    Sock serverSock;
    uint16_t port = atoi(argv[1]);
    // 服务器的只需要这个port就行
    InetAddress servaddr(port);
    // 创建非阻塞的sock
    serverSock.cerateNonBlockingSocket();
    serverSock.setKeepAlive(true);
    serverSock.setReuseAddr(true);
    serverSock.setReusePort(true);
    serverSock.settcpnodelay(true);
    serverSock.bind(servaddr);
    serverSock.listen();

    Epoll epoll;
    epoll.addfd(serverSock.getSock(), EPOLLIN | EPOLLET);
    // struct epoll_event ev;
    // ev.events = EPOLLIN | EPOLLET;
    // ev.data.fd = serverSock.getSock();
    // epoll_ctl(epollfd, EPOLL_CTL_ADD, serverSock.getSock(), &ev);
    // 需要从队列中拿出数据 存放epoll_wait()返回事件的数组。
    // struct epoll_event evs[10];

    // 最后一个式timeout = -1 等于阻塞
    //  = 0 立马返回，非阻塞
    //  大于0 就是隔几秒种看一下有事件wait没
    std::vector<epoll_event> evs;
    while (true)
    {

        evs = epoll.loop();

        for (auto &ev : evs)
        {
            if (ev.events & EPOLLRDHUP) // 这是一个事件标志，用于指示对端套接字（通常是客户端）已经关闭了连接或者半关闭了连接（即关闭了写操作）
            {
                printf("client(eventfd=%d) disconnected.\n", ev.data.fd);
                close(ev.data.fd); // 关闭客户端的fd。
            }
            else if (ev.events & (EPOLLIN | EPOLLPRI)) // 接收缓冲区中有数据可以读。
            {
                if (ev.data.fd == serverSock.getSock())
                {
                    InetAddress clientaddr;
                    Sock *clentSock = new Sock(serverSock.acceptNonBlocking(clientaddr));
                    printf("accept client(fd=%d,ip=%s,port=%d) ok.\n", clentSock->getSock(), clientaddr.ip(), clientaddr.port());
                    // ev.data.fd = clentSock->getSock();
                    // ev.events = EPOLLIN | EPOLLET; // 边缘触发。
                    // epoll_ctl(epollfd, EPOLL_CTL_ADD, clentSock->getSock(), &ev);
                    epoll.addfd(clentSock->getSock(),EPOLLIN | EPOLLET);
                }
                else
                {
                    char buffer[1024];
                    while (true)
                    {
                        // 这里酒不用设置最后一个为0了
                        bzero(&buffer, sizeof(buffer));
                        // ssize_t read(int fd, void *buf, size_t count);
                        ssize_t nread = read(ev.data.fd, buffer, sizeof(buffer) - 1);
                        if (nread > 0)
                        {
                            buffer[nread] = 0;
                            printf("recv(eventfd=%d):%s\n", ev.data.fd, buffer);
                            send(ev.data.fd, buffer, strlen(buffer), 0);
                        }
                        else if (nread == 0) // 是对方断开客户端了
                        {
                            printf("client(eventfd=%d) disconnected.\n", ev.data.fd);
                            close(ev.data.fd); // 关闭客户端的fd。
                            break;
                        }
                        else // n==-1
                        {
                            if (errno == EINTR)
                            {
                                continue;
                            }
                            else if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) // 没有错误 ，只是没有数据。
                            {
                                break;
                            }
                            else
                            {
                                // 真的出错了
                            }
                        }
                    }
                }
            }
            else if (ev.events & EPOLLOUT) // 有数据需要写，暂时没有代码，以后再说。
            {
                // ...
            }
            else
            {
                printf("client(eventfd=%d) error.\n", ev.data.fd);
                close(ev.data.fd); // 关闭客户端的fd。
            }
        }
    }

    return 0;
}